home *** CD-ROM | disk | FTP | other *** search
/ Gigarom 1 / Gigarom Macintosh Archives (Quantum Leap)(CDRM1080320)(1993).iso / FILES / DEV / A-B / 002. TESample.cpt / TESample.p < prev    next >
Text File  |  1988-08-01  |  38KB  |  1,276 lines

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    MultiFinder-Aware Simple TextEdit Sample Application
  6. #
  7. #    TESample
  8. #
  9. #    TESample.p    -    Pascal Source
  10. #
  11. #    Copyright © 1988 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.0                    8/88
  15. #
  16. #    Components:    TESample.p            August 1, 1988
  17. #                TESample.c            August 1, 1988
  18. #                TESample.a            August 1, 1988
  19. #                TESample.r            August 1, 1988
  20. #                TESample.h            August 1, 1988
  21. #                PTESample.make        August 1, 1988
  22. #                CTESample.make        August 1, 1988
  23. #
  24. #    TESample is an example application that demonstrates how 
  25. #    to initialize the commonly used toolbox managers, operate 
  26. #    successfully under MultiFinder, handle desk accessories and 
  27. #    create, grow, and zoom windows. The fundamental TextEdit 
  28. #    toolbox calls and TextEdit autoscroll are demonstrated. It 
  29. #    also shows how to create and maintain scrollbar controls.
  30. #
  31. #    It does not by any means demonstrate all the techniques you 
  32. #    need for a large application. In particular, Sample does not 
  33. #    cover exception handling, multiple windows/documents, 
  34. #    sophisticated memory management, printing, or undo. All of 
  35. #    these are vital parts of a normal full-sized application.
  36. #
  37. #    This application is an example of the form of a Macintosh 
  38. #    application; it is NOT a template. It is NOT intended to be 
  39. #    used as a foundation for the next world-class, best-selling, 
  40. #    600K application. A stick figure drawing of the human body may 
  41. #    be a good example of the form for a painting, but that does not 
  42. #    mean it should be used as the basis for the next Mona Lisa.
  43. #
  44. #    We recommend that you review this program or Sample before 
  45. #    beginning a new application. Sample is a simple app. which doesn’t 
  46. #    use TextEdit or the Control Manager.
  47. #
  48. ------------------------------------------------------------------------------}
  49.  
  50.  
  51. PROGRAM TESample;
  52.  
  53.  
  54. {Segmentation strategy:
  55.  
  56.  This program consists of three segments. Main contains most of the code,
  57.  including the MPW libraries, and the main program. Initialize contains
  58.  code that is only used once, during startup, and can be unloaded after the
  59.  program starts. %A5Init is automatically created by the Linker to initialize
  60.  globals for the MPW libraries and is unloaded right away.}
  61.  
  62.  
  63. {SetPort strategy:
  64.  
  65.  Toolbox routines do not change the current port. In spite of this, in this
  66.  program we use a strategy of calling SetPort whenever we want to draw or
  67.  make calls which depend on the current port. This makes us less vulnerable
  68.  to bugs in other software which might alter the current port (such as the
  69.  bug (feature?) in many desk accessories which change the port on OpenDeskAcc).
  70.  Hopefully, this also makes the routines from this program more self-contained,
  71.  since they don't depend on the current port setting.}
  72.  
  73.  
  74. {Clipboard strategy:
  75.  
  76.  This program does not maintain a private scrap. Whenever a cut, copy, or paste
  77.  occurs, we import/export from the public scrap to TextEdit's scrap right away,
  78.  using the TEToScrap and TEFromScrap routines. If we did use a private scrap,
  79.  the import/export would be in the activate/deactivate event and suspend/resume
  80.  event routines.}
  81.  
  82. {$D+}
  83.  
  84. USES
  85.     MemTypes, QuickDraw, OSIntf, ToolIntf;
  86.  
  87.  
  88. CONST
  89.     {MPW 3.0 will include a Traps.p interface file that includes constants for trap numbers.
  90.      These constants are from that file.}
  91.     _WaitNextEvent            = $A860;
  92.     _Unimplemented            = $A89F;
  93.  
  94.     {TextMargin is the number of pixels we leave blank at the edge of the window.}
  95.     textMargin                = 2;
  96.  
  97.     {MaxOpenDocuments is used to determine whether a new document can be opened
  98.      or created. We keep track of the number of open documents, and disable the
  99.      menu items that create a new document when the maximum is reached. If the
  100.      number of documents falls below the maximum, the items are enabled again.}
  101.     maxOpenDocuments        = 1;
  102.     
  103.     {MaxDocWidth is an arbitrary number used to specify the width of the TERec's
  104.     destination rectangle so that word wrap and horizontal scrolling can be
  105.     demonstrated.}
  106.     maxDocWidth                = 576;
  107.     
  108.     {MinDocWidth is used to limit the minimum dimension of a window when GrowWindow
  109.     is called.}
  110.     minDocDim                = 64;
  111.     
  112.     {ControlInvisible is used to 'turn off' controls (i.e., cause the control not
  113.     to be redrawn as a result of some Control Manager call such as SetCtlValue)
  114.     by being put into the contrlVis field of the record. ControlVisible is used
  115.     the same way to 'turn on' the control.}
  116.     controlInvisible        = 0;
  117.     controlVisible            = $FF;
  118.     
  119.     {ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
  120.     values for control positioning and sizing.}
  121.     scrollbarAdjust            = 15;
  122.     growboxAdjust            = 13;
  123.     scrollbarWidth            = 16;
  124.     
  125.     {CrCharacter is used to compare for a carriage return when calculating the
  126.     number of lines in the TextEdit record.}
  127.     crCharacter                = 13;
  128.     
  129.     {ButtonScroll is how many pixels to scroll horizontally when the button part
  130.     of the horizontal scrollbar is pressed.}
  131.     buttonScroll            = 4;
  132.     
  133.     {MaxTELength is an arbitrary number used to limit the length of text in the TERec
  134.     so that various errors won't occur from too many characters being in the text.}
  135.     maxTELength                = 32000;
  136.  
  137.     {SysEnvironsVersion is passed to SysEnvirons to tell it which version of the
  138.      SysEnvRec we understand.}
  139.     sysEnvironsVersion        = 1;
  140.  
  141.     {OSEvent is the event number of the suspend/resume and mouse-moved events sent
  142.      by MultiFinder. Once we determine that an event is an osEvent, we look at the
  143.      high byte of the message sent to determine which kind it is. To differentiate
  144.      suspend and resume events we check the resumeMask bit.}
  145.     osEvent                    = app4Evt;    {event used by MultiFinder}
  146.     suspendResumeMessage    = 1;        {high byte of suspend/resume event message}
  147.     resumeMask                = 1;        {bit of message field for resume vs. suspend}
  148.     mouseMovedMessage        = $FA;        {high byte of mouse-moved event message}
  149.  
  150.     {KMinSize is the minimum size (in K) for the application to run.}
  151.     kMinSize    = 40;
  152.     
  153.     {ExtremeNeg and ExtremePos are used to set up wide open rectangles and regions.}
  154.     extremeNeg                = -32768;
  155.     extremePos                = 32767 - 1;    {required for old region bug}
  156.     
  157.     {The following constants are all resource IDs, corresponding to resources in Sample.r.}
  158.     rMenuBar    = 128;                    {application's menu bar}
  159.     rAboutAlert    = 128;                    {about alert}
  160.     rEditAlert    = 129;                    {edit error alert}
  161.     rDocWindow    = 128;                    {application's window}
  162.     rVScroll    = 128;                    {vertical scrollbar control}
  163.     rHScroll    = 129;                    {horizontal scrollbar control}
  164.  
  165.     {The following constants are used to identify menus and their items. The menu IDs
  166.      have an "m" prefix and the item numbers within each menu have an "i" prefix.}
  167.     mApple        = 128;                    {Apple menu}
  168.     iAbout        = 1;
  169.  
  170.     mFile        = 129;                    {File menu}
  171.     iNew        = 1;
  172.     iClose        = 4;
  173.     iQuit        = 12;
  174.  
  175.     mEdit        = 130;                    {Edit menu}
  176.     iUndo        = 1;
  177.     iCut        = 3;
  178.     iCopy        = 4;
  179.     iPaste        = 5;
  180.     iClear        = 6;
  181.  
  182.  
  183. TYPE
  184.     {A DocumentRecord contains the WindowRecord for one of our document windows,
  185.      as well as the TEHandle for the text we are editing. We have added fields to
  186.      hold the ControlHandles to the vertical and horizontal scrollbars and to hold
  187.      the address of the default clikLoop that gets attached to a TERec when you call
  188.      TEAutoView. Other document fields can be added to this record as needed. For
  189.      a similar example, see how the Window Manager and Dialog Manager add fields
  190.      after the GrafPort.}
  191.     DocumentRecord    = RECORD
  192.         docWindow    : WindowRecord;
  193.         docTE        : TEHandle;
  194.         docVScroll    : ControlHandle;
  195.         docHScroll    : ControlHandle;
  196.         docClik        : ProcPtr;
  197.     END;
  198.     DocumentPeek    = ^DocumentRecord;
  199.  
  200.  
  201. VAR
  202.     {The "g" prefix is used to emphasize that a variable is global.}
  203.  
  204.     {GMac is used to hold the result of a SysEnvirons call. This makes
  205.      it convenient for any routine to check the environment.}
  206.     gMac                : SysEnvRec;    {set up by Initialize}
  207.  
  208.     {GHasWaitNextEvent is set at startup, and tells whether the WaitNextEvent
  209.      trap is available. If it is false, we know that we must call GetNextEvent.}
  210.     gHasWaitNextEvent    : BOOLEAN;        {set up by Initialize}
  211.  
  212.     {GInBackground is maintained by our osEvent handling routines. Any part of
  213.      the program can check it to find out if it is currently in the background.}
  214.     gInBackground        : BOOLEAN;        {maintained by Initialize and DoEvent}
  215.  
  216.     {GNumDocuments is used to keep track of how many open documents there are
  217.      at any time. It is maintained by the routines that open and close documents.}
  218.     gNumDocuments        : INTEGER;        {maintained by Initialize, DoNew, and DoCloseWindow}
  219.  
  220.     
  221. {$S Initialize}
  222. FUNCTION TrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
  223.  
  224. {Check to see if a given trap is implemented. This is only used by the
  225.  Initialize routine in this program, so we put it in the Initialize segment.}
  226.  
  227. BEGIN
  228.     {Check and see if the trap exists. On 64K ROM machines, tType will be ignored.}
  229.  
  230.     TrapAvailable := NGetTrapAddress(tNumber, tType) <> GetTrapAddress(_Unimplemented);
  231. END; {TrapAvailable}
  232.  
  233.  
  234. {$S Main}
  235. FUNCTION IsDAWindow(window: WindowPtr): BOOLEAN;
  236.  
  237. {Check if a window belongs to a desk accessory.}
  238.  
  239. BEGIN
  240.     IF window = NIL THEN
  241.         IsDAWindow := FALSE
  242.     ELSE    {DA windows have negative windowKinds}
  243.         IsDAWindow := WindowPeek(window)^.windowKind < 0;
  244. END; {IsDAWindow}
  245.  
  246.  
  247. {$S Main}
  248. FUNCTION IsAppWindow(window: WindowPtr): BOOLEAN;
  249.  
  250. {Check if a window belongs to the application.}
  251.  
  252. BEGIN
  253.     IF window = NIL THEN
  254.         IsAppWindow := FALSE
  255.     ELSE    {application windows have non-negative windowKinds}
  256.         IsAppWindow := WindowPeek(window)^.windowKind >= 0;
  257. END; {IsAppWindow}
  258.  
  259.  
  260. {$S Main}
  261. PROCEDURE GetTERect(window: WindowPtr; VAR teRect: Rect);
  262.  
  263. {return a rectangle that is inset from the portRect by the size of
  264. the scrollbars and a little extra margin.}
  265.  
  266. BEGIN
  267.     teRect := window^.portRect;
  268.     InsetRect(teRect, textMargin, textMargin);            {adjust for margin}
  269.     teRect.bottom := teRect.bottom - scrollbarAdjust;    {and for the scrollbars}
  270.     teRect.right := teRect.right - scrollbarAdjust;
  271. END; {GetTERect}
  272.  
  273.  
  274. {$S Main}
  275. PROCEDURE DoCloseWindow(window: WindowPtr);
  276.  
  277. {Close a window. This handles desk accessory and application windows.}
  278.  
  279. BEGIN
  280.     IF IsDAWindow(window) THEN
  281.         CloseDeskAcc(WindowPeek(window)^.windowKind)
  282.     ELSE IF IsAppWindow(window) THEN BEGIN
  283.         WITH DocumentPeek(window)^ DO
  284.             IF docTE <> NIL THEN
  285.                 TEDispose(docTE);        {dispose the TEHandle}
  286.         DisposeWindow(window);
  287.         gNumDocuments := gNumDocuments - 1;
  288.     END;
  289. END; {DoCloseWindow}
  290.  
  291.  
  292. {$S Main}
  293. PROCEDURE AdjustViewRect(docTE: TEHandle);
  294.  
  295. {Update the TERec's view rect so that it is the greatest multiple of
  296. the lineHeight and still fits in the old viewRect.}
  297.  
  298. BEGIN
  299.     WITH docTE^^ DO BEGIN
  300.         viewRect.bottom := (((viewRect.bottom - viewRect.top) DIV lineHeight) *
  301.                             lineHeight) + viewRect.top;
  302.     END;
  303. END; {AdjustViewRect}
  304.  
  305.  
  306. {$S Main}
  307. PROCEDURE AdjustTE(window: WindowPtr);
  308.  
  309. {Scroll the TERec around to match up to the potentially updated scrollbar
  310. values. This is really useful when the window resizes such that the
  311. scrollbars become inactive and the TERec had been previously scrolled.}
  312.  
  313. VAR
  314.     value    : INTEGER;
  315. BEGIN
  316.     WITH DocumentPeek(window)^ DO BEGIN
  317.         TEScroll((docTE^^.viewRect.left - docTE^^.destRect.left) - GetCtlValue(docHScroll),
  318.                 (docTE^^.viewRect.top - docTE^^.destRect.top) -
  319.                     (GetCtlValue(docVScroll) * docTE^^.lineHeight),
  320.                 docTE);
  321.     END;
  322. END; {AdjustTE}
  323.  
  324.  
  325. {$S Main}
  326. PROCEDURE AdjustHV(isVert: BOOLEAN; control: ControlHandle; docTE: TEHandle; canRedraw: BOOLEAN);
  327.  
  328. {Calculate the new control maximum value and current value, whether it is the horizontal or
  329. vertical scrollbar. The vertical max is calculated by comparing the number of lines to the
  330. vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document
  331. width to the width of the viewRect. The current values are set by comparing the offset between
  332. the view and destination rects. If necessary and we canRedraw, have the control be re-drawn by
  333. calling ShowControl.}
  334.  
  335. VAR
  336.     value, lines, max    : INTEGER;
  337.     oldValue, oldMax    : INTEGER;
  338. BEGIN
  339.     oldValue := GetCtlValue(control);
  340.     oldMax := GetCtlMax(control);
  341.     IF isVert THEN BEGIN
  342.         lines := docTE^^.nLines;
  343.         {since nLines isn’t right if the last character is a return, check for that case}
  344.         IF Ptr(ORD(docTE^^.hText^) + docTE^^.teLength - 1)^ = crCharacter THEN
  345.             lines := lines + 1;
  346.         max := lines - ((docTE^^.viewRect.bottom - docTE^^.viewRect.top) DIV docTE^^.lineHeight);
  347.     END ELSE
  348.         max := maxDocWidth - (docTE^^.viewRect.right - docTE^^.viewRect.left);
  349.     IF max < 0 THEN max := 0;            {check for negative values}
  350.     SetCtlMax(control, max);
  351.     IF isVert THEN
  352.         value := (docTE^^.viewRect.top - docTE^^.destRect.top) DIV docTE^^.lineHeight
  353.     ELSE
  354.         value := docTE^^.viewRect.left - docTE^^.destRect.left;
  355.     IF value < 0 THEN
  356.         value := 0
  357.     ELSE IF value > max THEN
  358.         value := max;                    {pin the value to within range}
  359.     SetCtlValue(control, value);
  360.     IF canRedraw & ( (max <> oldMax) | (value <> oldValue) ) THEN
  361.         ShowControl(control);            {check to see if the control can be re-drawn}
  362. END; {AdjustHV}
  363.  
  364.  
  365. {$S Main}
  366. PROCEDURE AdjustScrollValues(window: WindowPtr; canRedraw: BOOLEAN);
  367.  
  368. {Simply call the common adjust routine for the vertical and horizontal scrollbars.}
  369.  
  370. BEGIN
  371.     WITH DocumentPeek(window)^ DO BEGIN
  372.         AdjustHV(TRUE, docVScroll, docTE, canRedraw);
  373.         AdjustHV(FALSE, docHScroll, docTE, canRedraw);
  374.     END;
  375. END; {AdjustScrollValues}
  376.  
  377.  
  378. {$S Main}
  379. PROCEDURE AdjustScrollSizes(window: WindowPtr);
  380.  
  381. {Re-calculate the position and size of the viewRect and the scrollbars.}
  382.  
  383. VAR
  384.     teRect    : Rect;
  385. BEGIN
  386.     GetTERect(window, teRect);
  387.     WITH DocumentPeek(window)^, window^.portRect DO BEGIN
  388.         docTE^^.viewRect := teRect;
  389.         AdjustViewRect(docTE);
  390.         MoveControl(docVScroll, right - scrollbarAdjust, -1);
  391.         SizeControl(docVScroll, scrollbarWidth, (bottom - top) - growboxAdjust);
  392.         MoveControl(docHScroll, -1, bottom - scrollbarAdjust);
  393.         SizeControl(docHScroll, (right - left) - growboxAdjust, scrollbarWidth);
  394.     END;
  395. END; {AdjustScrollSizes}
  396.  
  397.  
  398. {$S Main}
  399. PROCEDURE AdjustScrollbars(window: WindowPtr; needsResize: BOOLEAN);
  400.  
  401. {Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
  402. and we don't want that). If the controls are to be resized as well, call the procedure to do that,
  403. then call the procedure to adjust the maximum and current values. Finally re-enable the controls
  404. by jamming a $FF in their contrlVis fields.}
  405.  
  406. VAR
  407.     oldMax, oldVal    : INTEGER;
  408. BEGIN
  409.     WITH DocumentPeek(window)^ DO BEGIN
  410.         {First, turn visibility of scrollbars off so we won’t get unwanted redrawing}
  411.         docVScroll^^.contrlVis := controlInvisible;        {turn them off}
  412.         docHScroll^^.contrlVis := controlInvisible;
  413.         IF needsResize THEN                                {move and size if needed}
  414.             AdjustScrollSizes(window);
  415.         AdjustScrollValues(window, NOT needsResize);    {fool with max and current value}
  416.         {Now, restore visibility in case we never had to ShowControl during adjustment}
  417.         docVScroll^^.contrlVis := controlVisible;        {turn them on}
  418.         docHScroll^^.contrlVis := controlVisible;
  419.     END;
  420. END; {AdjustScrollbars}
  421.  
  422.  
  423. {$S Main}
  424. {$PUSH} {$Z+}
  425. PROCEDURE PascalClikLoop;
  426.  
  427. {Gets called from our assembly language routine, AsmClikLoop, which is in
  428.  turn called by the TEClick toolbox routine. Saves the windows clip region,
  429.  sets it to the portRect, adjusts the scrollbar values to match the TE scroll
  430.  amount, then restores the clip region.}
  431.  
  432. VAR
  433.     window    : WindowPtr;
  434.     region    : RgnHandle;
  435. BEGIN
  436.     window := FrontWindow;
  437.     region := NewRgn;
  438.     GetClip(region);                    {save the old clip}
  439.     ClipRect(window^.portRect);            {set the new clip}
  440.     AdjustScrollValues(window, TRUE);    {pass TRUE for canRedraw}
  441.     SetClip(region);                    {restore the old clip}
  442.     DisposeRgn(region);
  443. END; {PascalClikLoop}
  444. {$POP}
  445.  
  446.  
  447. {$S Main}
  448. {$PUSH} {$Z+}
  449. FUNCTION GetOldClikLoop : ProcPtr;
  450.  
  451. {Gets called from our assembly language routine, AsmClikLoop, which is in
  452. turn called by the TEClick toolbox routine. It returns the address of the
  453. default clikLoop routine that was put into the TERec by TEAutoView to
  454. AsmClikLoop so that it can call it.}
  455.  
  456. BEGIN
  457.     GetOldClikLoop := DocumentPeek(FrontWindow)^.docClik;
  458. END; {GetOldClikLoop}
  459. {$POP}
  460.  
  461.  
  462. PROCEDURE AsmClikLoop; EXTERNAL;
  463.  
  464. {A reference to our assembly language routine that gets attached to the clikLoop
  465. field of our TE record.}
  466.  
  467.  
  468. {$S Main}
  469. PROCEDURE DoNew;
  470.  
  471. {Create a new document and window.}
  472.  
  473. VAR
  474.     good                : BOOLEAN;
  475.     storage                : Ptr;
  476.     window                : WindowPtr;
  477.     destRect, viewRect    : Rect;
  478.  
  479. BEGIN
  480.     storage := NewPtr(SIZEOF(DocumentRecord));
  481.     IF storage <> NIL THEN BEGIN
  482.         window := GetNewWindow(rDocWindow, storage, WindowPtr(-1));
  483.         IF window <> NIL THEN BEGIN
  484.             gNumDocuments := gNumDocuments + 1;    {this will be decremented when we call DoCloseWindow}
  485.             good := FALSE;
  486.             SetPort(window);
  487.             WITH window^, DocumentPeek(window)^ DO BEGIN
  488.                 GetTERect(window, viewRect);
  489.                 destRect := viewRect;
  490.                 destRect.right := destRect.left + maxDocWidth;
  491.                 docTE := TENew(destRect, viewRect);
  492.                 AdjustViewRect(docTE);
  493.                 TEAutoView(TRUE, docTE);
  494.                 docClik := docTE^^.clikLoop;
  495.                 docTE^^.clikLoop := @AsmClikLoop;
  496.                 IF docTE <> NIL THEN
  497.                     good := TRUE;                {if TENew succeeded, we have a good document}
  498.                 IF good THEN BEGIN
  499.                     docVScroll := GetNewControl(rVScroll, window);
  500.                     good := (docVScroll <> NIL);
  501.                 END;
  502.                 IF good THEN BEGIN
  503.                     docHScroll := GetNewControl(rHScroll, window);
  504.                     good := (docHScroll <> NIL);
  505.                 END;
  506.                 IF good THEN BEGIN
  507.                     {FALSE to AdjustScrollValues means musn’t redraw; technically, of course,
  508.                     the window is hidden so it wouldn’t matter whether we called ShowControl or not.}
  509.                     AdjustScrollValues(window, FALSE);
  510.                     ShowWindow(window);            {if the document is good, make the window visible}
  511.                 END ELSE BEGIN
  512.                     DoCloseWindow(window);        {otherwise regret we ever created it...}
  513.                     SysBeep(8);                    {and beep (8 is an arbitrary duration)}
  514.                 END
  515.             END;
  516.         END ELSE
  517.             DisposPtr(storage);                    {get rid of the storage if it is never used}
  518.     END;
  519. END; {DoNew}
  520.  
  521.  
  522. {$S Main}
  523. PROCEDURE ForceEnvirons;
  524.  
  525. {Make sure that the machine has at least 128K ROMs and enough memory to run.
  526. If it doesn't, exit.}
  527.  
  528. VAR
  529.     ignoreError    : OSErr;
  530. BEGIN
  531.     {ignore the error returned from SysEnvirons; even if an error occurred,
  532.      the SysEnvirons glue will fill in the SysEnvRec}
  533.     ignoreError := SysEnvirons(sysEnvironsVersion, gMac);
  534.     IF (gMac.machineType < 0) |
  535.         (StackSpace + ORD(GetApplLimit) - ORD(ApplicZone) < kMinSize * 1024) THEN
  536.         ExitToShell;
  537.     {if you have stack requirements that differ from the default,
  538.     then you could use SetApplLimit to increase StackSpace at this point.}
  539. END; {ForceEnvirons}
  540.  
  541.  
  542. {$S Initialize}
  543. PROCEDURE Initialize;
  544.  
  545. {Set up the whole world, including global variables, Toolbox managers,
  546.  menus, and a single blank document.}
  547.  
  548. VAR
  549.     menuBar        : Handle;
  550.  
  551. BEGIN
  552.     gHasWaitNextEvent := TrapAvailable(_WaitNextEvent, ToolTrap);
  553.     gInBackground := FALSE;
  554.  
  555.     InitGraf(@thePort);
  556.     InitFonts;
  557.     InitWindows;
  558.     InitMenus;
  559.     TEInit;
  560.     InitDialogs(NIL);
  561.     InitCursor;
  562.  
  563.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  564.     IF menuBar = NIL THEN ExitToShell;
  565.     SetMenuBar(menuBar);                    {install menus}
  566.     DisposHandle(menuBar);
  567.     AddResMenu(GetMHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  568.     DrawMenuBar;
  569.  
  570.     gNumDocuments := 0;
  571.  
  572.     {do other initialization here}
  573.  
  574.     DoNew;                                    {create a single empty document}
  575. END; {Initialize}
  576.  
  577.  
  578. {$S Main}
  579. PROCEDURE DoCloseBehind(window: WindowPtr);
  580.  
  581. {Close the window that is passed and all windows behind it.
  582.  This closes windows from back to front, by calling itself
  583.  recursively, which minimizes window updating.}
  584.  
  585. BEGIN
  586.     IF window <> NIL THEN BEGIN    {if we are passed a window, close other windows behind it first}
  587.         DoCloseBehind(WindowPtr(WindowPeek(window)^.nextWindow));
  588.         DoCloseWindow(window);    {now that all the windows behind are closed, close this one}
  589.     END;
  590. END; {DoCloseBehind}
  591.  
  592.  
  593. {$S Main}
  594. PROCEDURE Terminate;
  595.  
  596. {Clean up the application and exits. We close all of the windows so that
  597.  they can update their documents.}
  598.  
  599. BEGIN
  600.     DoCloseBehind(FrontWindow);    {close all windows}
  601.     ExitToShell;
  602. END; {Terminate}
  603.  
  604.  
  605. {$S Main}
  606. PROCEDURE AdjustMenus;
  607.  
  608. {Enable and disable menus based on the current state.
  609.  The user can only select enabled menu items. We set up all the menu items
  610.  before calling MenuSelect or MenuKey, since these are the only times that
  611.  a menu item can be selected. Note that MenuSelect is also the only time
  612.  the user will see menu items.}
  613.  
  614. VAR
  615.     window            : WindowPtr;
  616.     menu            : MenuHandle;
  617.     offset            : LONGINT;
  618.     undo            : BOOLEAN;
  619.     cutCopyClear    : BOOLEAN;
  620.     paste            : BOOLEAN;
  621.  
  622. BEGIN
  623.     window := FrontWindow;
  624.  
  625.     menu := GetMHandle(mFile);
  626.     IF gNumDocuments < maxOpenDocuments THEN
  627.         EnableItem(menu, iNew)        {New is enabled when we can open more documents}
  628.     ELSE
  629.         DisableItem(menu, iNew);
  630.     IF window <> NIL THEN            {Close is enabled when there is a window to close}
  631.         EnableItem(menu, iClose)
  632.     ELSE
  633.         DisableItem(menu, iClose);
  634.  
  635.     menu := GetMHandle(mEdit);
  636.     undo := FALSE;
  637.     cutCopyClear := FALSE;
  638.     paste := FALSE;
  639.     IF IsDAWindow(window) THEN BEGIN
  640.         undo := TRUE;                {all editing is enabled for DA windows}
  641.         cutCopyClear := TRUE;
  642.         paste := TRUE;
  643.     END ELSE IF IsAppWindow(window) THEN BEGIN
  644.         WITH DocumentPeek(window)^.docTE^^ DO
  645.             IF selStart < selEnd THEN
  646.                 cutCopyClear := TRUE;
  647.                 {Cut, Copy, and Clear is enabled for app. windows with selections}
  648.         IF GetScrap(NIL, 'TEXT', offset) > 0 THEN
  649.             paste := TRUE;            {Paste is enabled for app. windows}
  650.     END;
  651.     IF undo THEN
  652.         EnableItem(menu, iUndo)
  653.     ELSE
  654.         DisableItem(menu, iUndo);
  655.     IF cutCopyClear THEN BEGIN
  656.         EnableItem(menu, iCut);
  657.         EnableItem(menu, iCopy);
  658.         EnableItem(menu, iClear);
  659.     END ELSE BEGIN
  660.         DisableItem(menu, iCut);
  661.         DisableItem(menu, iCopy);
  662.         DisableItem(menu, iClear);
  663.     END;
  664.     IF paste THEN
  665.         EnableItem(menu, iPaste)
  666.     ELSE
  667.         DisableItem(menu, iPaste);
  668. END; {AdjustMenus}
  669.  
  670.  
  671. {$S Main}
  672. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  673.  
  674. {This is called when an item is chosen from the menu bar (after calling
  675.  MenuSelect or MenuKey). It does the right thing for each command.}
  676.  
  677. VAR
  678.     menuID            : INTEGER;
  679.     menuItem        : INTEGER;
  680.     itemHit            : INTEGER;
  681.     daName            : Str255;
  682.     daRefNum        : INTEGER;
  683.     ignoreResult    : OSErr;
  684.     handledByDA        : BOOLEAN;
  685.     te                : TEHandle;
  686.     window            : WindowPtr;
  687.  
  688. BEGIN
  689.     window := FrontWindow;
  690.     menuID := HiWrd(menuResult);    {use built-ins (for efficiency)...}
  691.     menuItem := LoWrd(menuResult);    {to get menu item number and menu number}
  692.     CASE menuID OF
  693.         mApple:
  694.             CASE menuItem OF
  695.                 iAbout:                {bring up alert for About}
  696.                     itemHit := Alert(rAboutAlert, NIL);
  697.                 OTHERWISE BEGIN        {all non-About items in this menu are DAs}
  698.                     GetItem(GetMHandle(mApple), menuItem, daName);
  699.                     daRefNum := OpenDeskAcc(daName);
  700.                 END;
  701.             END;
  702.         mFile:
  703.             CASE menuItem OF
  704.                 iNew:
  705.                     DoNew;
  706.                 iClose:
  707.                     DoCloseWindow(window);
  708.                 iQuit:
  709.                     Terminate;
  710.             END;
  711.         mEdit: BEGIN                {call SystemEdit for DA editing & MultiFinder}
  712.             IF NOT SystemEdit(menuItem-1) THEN BEGIN
  713.                 te := DocumentPeek(window)^.docTE;
  714.                 CASE menuItem OF
  715.                     iCut: BEGIN        {after cutting, export the TE scrap}
  716.                         IF ZeroScrap = noErr THEN BEGIN
  717.                             TECut(te);
  718.                             IF TEToScrap <> noErr THEN BEGIN
  719.                                 SetCursor(arrow);
  720.                                 itemHit := Alert(rEditAlert, NIL);
  721.                                 ignoreResult := ZeroScrap;
  722.                             END;
  723.                         END;
  724.                     END;
  725.                     iCopy: BEGIN    {after copying, export the TE scrap}
  726.                         IF ZeroScrap = noErr THEN BEGIN
  727.                             TECopy(te);
  728.                             IF TEToScrap <> noErr THEN BEGIN
  729.                                 SetCursor(arrow);
  730.                                 itemHit := Alert(rEditAlert, NIL);
  731.                                 ignoreResult := ZeroScrap;
  732.                             END;
  733.                         END;
  734.                     END;
  735.                     iPaste: BEGIN    {import the TE scrap before pasting}
  736.                         IF TEFromScrap = noErr THEN BEGIN
  737.                             IF TEGetScrapLen + (te^^.teLength -
  738.                                 (te^^.selEnd - te^^.selStart)) < maxTELength THEN
  739.                                 TEPaste(te)
  740.                             ELSE BEGIN
  741.                                 SetCursor(arrow);
  742.                                 itemHit := Alert(rEditAlert, NIL);
  743.                             END;
  744.                         END;
  745.                     END;
  746.                     iClear:
  747.                         TEDelete(te);
  748.                 END;
  749.                 AdjustScrollbars(window, FALSE);
  750.                 AdjustTE(window);
  751.             END;
  752.         END;
  753.     END;
  754.     HiliteMenu(0);                    {unhighlight what MenuSelect (or MenuKey) hilited}
  755. END; {DoMenuCommand}
  756.  
  757.  
  758. {$S Main}
  759. PROCEDURE DrawWindow(window: WindowPtr);
  760.  
  761. {Draw the contents of an application window.}
  762.  
  763. BEGIN
  764.     SetPort(window);
  765.     WITH window^ DO BEGIN
  766.         EraseRect(portRect);        {as per TextEdit chapter of Inside Macintosh}
  767.         DrawControls(window);        {this ordering makes for a better appearance}
  768.         DrawGrowIcon(window);
  769.         TEUpdate(portRect, DocumentPeek(window)^.docTE);
  770.     END;
  771. END; {DrawWindow}
  772.  
  773.  
  774. {$S Main}
  775. FUNCTION GetSleep: LONGINT;
  776.  
  777. {Calculate a sleep value for WaitNextEvent. This takes into account the things
  778.  that DoIdle does with idle time.}
  779.  
  780. VAR
  781.     sleep    : LONGINT;
  782.     window    : WindowPtr;
  783.  
  784. BEGIN
  785.     sleep := MAXLONGINT;                    {default value for sleep}
  786.     IF NOT gInBackground THEN BEGIN            {if we are in front...}
  787.         window := FrontWindow;            {and the front window is ours...}
  788.         IF IsAppWindow(window) THEN BEGIN
  789.             WITH DocumentPeek(window)^.docTE^^ DO
  790.                 IF selStart = selEnd THEN    {and the selection is an insertion point...}
  791.                     sleep := GetCaretTime;    {we need to blink the insertion point}
  792.         END;
  793.     END;
  794.     GetSleep := sleep;
  795. END; {GetSleep}
  796.  
  797.  
  798. {$S Main}
  799. PROCEDURE CommonAction(control: ControlHandle; VAR amount: INTEGER);
  800.  
  801. {Common algorithm for setting the new value of a control. It returns the actual amount
  802. the value of the control changed. Note the pinning is done for the sake of returning
  803. the amount the control value changed.}
  804.  
  805. VAR
  806.     value, max    : INTEGER;
  807.     window        : WindowPtr;
  808. BEGIN
  809.     value := GetCtlValue(control);    {get current value}
  810.     max := GetCtlMax(control);        {and max value}
  811.     amount := value - amount;
  812.     IF amount < 0 THEN
  813.         amount := 0
  814.     ELSE IF amount > max THEN
  815.         amount := max;
  816.     SetCtlValue(control, amount);
  817.     amount := value - amount;        {calculate true change}
  818. END; {CommonAction}
  819.  
  820.  
  821. {$S Main}
  822. PROCEDURE VActionProc(control: ControlHandle; part: INTEGER);
  823.  
  824. {Determines how much to change the value of the vertical scrollbar by and how
  825. much to scroll the TE record.}
  826.  
  827. VAR
  828.     amount    : INTEGER;
  829.     window    : WindowPtr;
  830. BEGIN
  831.     IF part <> 0 THEN BEGIN
  832.         window := control^^.contrlOwner;
  833.         WITH DocumentPeek(window)^, DocumentPeek(window)^.docTE^^ DO BEGIN
  834.             CASE part OF
  835.                 inUpButton, inDownButton:
  836.                     amount := 1;                                                {one line}
  837.                 inPageUp, inPageDown:
  838.                     amount := (viewRect.bottom - viewRect.top) DIV lineHeight;    {one page}
  839.             END;
  840.             IF (part = inDownButton) | (part = inPageDown) THEN
  841.                 amount := -amount;                                                {reverse direction}
  842.             CommonAction(control, amount);
  843.             IF amount <> 0 THEN
  844.                 TEScroll(0, amount * docTE^^.lineHeight, docTE);
  845.         END;
  846.     END;
  847. END; {VActionProc}
  848.  
  849.  
  850. {$S Main}
  851. PROCEDURE HActionProc(control: ControlHandle; part: INTEGER);
  852.  
  853. {Determines how much to change the value of the horizontal scrollbar by and how
  854. much to scroll the TE record.}
  855.  
  856. VAR
  857.     amount    : INTEGER;
  858.     window    : WindowPtr;
  859. BEGIN
  860.     IF part <> 0 THEN BEGIN
  861.         window := control^^.contrlOwner;
  862.         WITH DocumentPeek(window)^, DocumentPeek(window)^.docTE^^ DO BEGIN
  863.             CASE part OF
  864.                 inUpButton, inDownButton:                                        {a few pixels}
  865.                     amount := buttonScroll;
  866.                 inPageUp, inPageDown:
  867.                     amount := viewRect.right - viewRect.left;                    {a page}
  868.             END;
  869.             IF (part = inDownButton) | (part = inPageDown) THEN
  870.                 amount := -amount;                                                {reverse direction}
  871.             CommonAction(control, amount);
  872.             IF amount <> 0 THEN
  873.                 TEScroll(amount, 0, docTE);
  874.         END;
  875.     END;
  876. END; {HActionProc}
  877.  
  878.  
  879. {$S Main}
  880. PROCEDURE DoIdle;
  881.  
  882. {This is called whenever we get an null event or a mouse-moved event.
  883.  It takes care of necessary periodic actions. For this program, it calls TEIdle.}
  884.  
  885. VAR
  886.     window    : WindowPtr;
  887.  
  888. BEGIN
  889.     window := FrontWindow;
  890.     IF IsAppWindow(window) THEN
  891.         TEIdle(DocumentPeek(window)^.docTE);
  892. END; {DoIdle}
  893.  
  894.  
  895. {$S Main}
  896. PROCEDURE DoKeyDown(event: EventRecord);
  897.  
  898. {This is called for any keyDown or autoKey events, except when the
  899.  Command key is held down. It looks at the frontmost window to decide what
  900.  to do with the key typed.}
  901.  
  902. VAR
  903.     window    : WindowPtr;
  904.     key        : CHAR;
  905.     te        : TEHandle;
  906.  
  907. BEGIN
  908.     window := FrontWindow;
  909.     IF IsAppWindow(window) THEN BEGIN
  910.         te := DocumentPeek(window)^.docTE;
  911.         key := CHR(BAnd(event.message, charCodeMask));
  912.         IF te^^.teLength - (te^^.selEnd - te^^.selStart) + 1 < maxTELength THEN BEGIN
  913.             TEKey(key, te);
  914.             AdjustScrollbars(window, FALSE);
  915.             AdjustTE(window);
  916.         END ELSE
  917.             SysBeep(8);
  918.     END;
  919. END; {DoKeyDown}
  920.  
  921.  
  922. {$S Main}
  923. PROCEDURE DoContentClick(window: WindowPtr; event: EventRecord);
  924.  
  925. {Called when a mouseDown occurs in the content of a window.}
  926.  
  927. VAR
  928.     mouse        : Point;
  929.     control        : ControlHandle;
  930.     part, value    : INTEGER;
  931.     shiftDown    : BOOLEAN;
  932.  
  933. BEGIN
  934.     IF IsAppWindow(window) THEN BEGIN
  935.         SetPort(window);
  936.         mouse := event.where;                                            {get the click position}
  937.         GlobalToLocal(mouse);                                            {convert to local coordinates}
  938.         part := FindControl(mouse, window, control);
  939.         WITH DocumentPeek(window)^ DO
  940.             CASE part OF
  941.                 0: BEGIN
  942.                     shiftDown := BAnd(event.modifiers, shiftKey) <> 0;    {extend if Shift is down}
  943.                     TEClick(mouse, shiftDown, docTE);
  944.                 END;
  945.                 inThumb: BEGIN
  946.                     value := GetCtlValue(control);
  947.                     part := TrackControl(control, mouse, NIL);
  948.                     IF part <> 0 THEN BEGIN
  949.                         value := value - GetCtlValue(control);
  950.                         IF value <> 0 THEN
  951.                             IF control = docVScroll THEN
  952.                                 TEScroll(0, value * docTE^^.lineHeight, docTE)
  953.                             ELSE
  954.                                 TEScroll(value, 0, docTE);
  955.                     END;
  956.                 END;
  957.                 OTHERWISE
  958.                     IF control = docVScroll THEN
  959.                         value := TrackControl(control, mouse, @VActionProc)
  960.                     ELSE
  961.                         value := TrackControl(control, mouse, @HActionProc);
  962.             END;
  963.     END;
  964. END; {DoContentClick}
  965.  
  966.  
  967. {$S Main}
  968. PROCEDURE ResizeWindow(window: WindowPtr);
  969.  
  970. {Called when the window has been resized to fix up the controls and content}
  971.  
  972. BEGIN
  973.     WITH window^ DO BEGIN
  974.         AdjustScrollbars(window, TRUE);
  975.         AdjustTE(window);
  976.         InvalRect(portRect);
  977.     END;
  978. END; {ResizeWindow}
  979.  
  980.  
  981. {$S Main}
  982. PROCEDURE GetLocalUpdateRgn(window: WindowPtr; localRgn: RgnHandle);
  983.  
  984. {Returns the update region in local coordinates}
  985.  
  986. BEGIN
  987.     CopyRgn(WindowPeek(window)^.updateRgn, localRgn);                        {save old update region}
  988.     WITH window^.portBits.bounds DO
  989.         OffsetRgn(localRgn, left, top);                                        {convert to local coords}
  990. END; {GetLocalUpdateRgn}
  991.  
  992.  
  993. {$S Main}
  994. PROCEDURE DoGrowWindow(window: WindowPtr; event: EventRecord);
  995.  
  996. {Called when a mouseDown occurs in the grow box of an active window. In
  997.  order to eliminate any 'flicker', we want to invalidate only what is
  998.  necessary. Since ResizeWindow invalidates the whole portRect, we save
  999.  the old TE viewRect, intersect it with the new TE viewRect, and
  1000.  remove the result from the update region. However, we must make sure
  1001.  that any old update region that might have been around gets put back.}
  1002.  
  1003. VAR
  1004.     growResult        : LONGINT;
  1005.     tempRect        : Rect;
  1006.     tempRgn            : RgnHandle;
  1007.     ignoreResult    : BOOLEAN;
  1008.  
  1009. BEGIN
  1010.     WITH screenBits.bounds DO
  1011.         SetRect(tempRect, minDocDim, minDocDim, right, bottom);                {set up limiting values}
  1012.     growResult := GrowWindow(window, event.where, tempRect);
  1013.     IF growResult <> 0 THEN                                                 {see if changed size}
  1014.         WITH DocumentPeek(window)^, window^ DO BEGIN
  1015.             tempRect := docTE^^.viewRect;                                    {save old text box}
  1016.             tempRgn := NewRgn;
  1017.             GetLocalUpdateRgn(window, tempRgn);                                {get localized update region}
  1018.             SizeWindow(window, LoWrd(growResult), HiWrd(growResult), TRUE);
  1019.             ResizeWindow(window);
  1020.             ignoreResult := SectRect(tempRect, docTE^^.viewRect, tempRect);    {find what stayed same}
  1021.             ValidRect(tempRect);                                            {take it out of update}
  1022.             InvalRgn(tempRgn);                                                {put back any prior update}
  1023.             DisposeRgn(tempRgn);
  1024.         END;
  1025. END; {DoGrowWindow}
  1026.  
  1027.  
  1028. {$S Main}
  1029. PROCEDURE DoZoomWindow(window: WindowPtr; part: INTEGER);
  1030.  
  1031. {Called when a mouseClick occurs in the zoom box of an active window.
  1032.  Everything has to get re-drawn here, so we don't mind that
  1033.  ResizeWindow invalidates the whole portRect.}
  1034.  
  1035. BEGIN
  1036.     WITH window^ DO BEGIN
  1037.         EraseRect(portRect);
  1038.         ZoomWindow(window, part, (window = FrontWindow));
  1039.         ResizeWindow(window);
  1040.     END;
  1041. END; {DoZoomWindow}
  1042.  
  1043.  
  1044. {$S Main}
  1045. PROCEDURE DoUpdate(window: WindowPtr);
  1046.  
  1047. {This is called when an update event is received for a window.
  1048.  It calls DrawWindow to draw the contents of an application window,
  1049.  but only if the visRgn is non-empty; for efficiency reasons,
  1050.  not because it is required.}
  1051.  
  1052. BEGIN
  1053.     IF IsAppWindow(window) THEN BEGIN
  1054.         BeginUpdate(window);                    {this sets up the visRgn}
  1055.         IF NOT EmptyRgn(window^.visRgn) THEN    {draw if updating needs to be done}
  1056.             DrawWindow(window);
  1057.         EndUpdate(window);
  1058.     END;
  1059. END; {DoUpdate}
  1060.  
  1061.  
  1062. {$S Main}
  1063. PROCEDURE DoActivate(window: WindowPtr; becomingActive: BOOLEAN);
  1064.  
  1065. {This is called when a window is activated or deactivated.}
  1066.  
  1067. VAR
  1068.     tempRgn        : RgnHandle;
  1069.     clipRgn        : RgnHandle;
  1070.  
  1071. BEGIN
  1072.     IF IsAppWindow(window) THEN
  1073.         WITH DocumentPeek(window)^ DO
  1074.             IF becomingActive THEN BEGIN
  1075.                 {since we don’t want TEActivate to draw a selection in an area where
  1076.                  we’re going to erase and redraw, we’ll clip out the update region
  1077.                  before calling it.}
  1078.                 tempRgn := NewRgn;
  1079.                 clipRgn := NewRgn;
  1080.                 GetLocalUpdateRgn(window, tempRgn);            {get localized update region}
  1081.                 GetClip(clipRgn);
  1082.                 DiffRgn(clipRgn, tempRgn, tempRgn);            {subtract updateRgn from clipRgn}
  1083.                 SetClip(tempRgn);
  1084.                 TEActivate(docTE);                            {let TE do its thing}
  1085.                 SetClip(clipRgn);                            {restore the full-blown clipRgn}
  1086.                 DisposeRgn(tempRgn);
  1087.                 DisposeRgn(clipRgn);
  1088.  
  1089.                 {the controls must be redrawn on activation:}
  1090.                 docVScroll^^.contrlVis := controlVisible;
  1091.                 docHScroll^^.contrlVis := controlVisible;
  1092.                 InvalRect(docVScroll^^.contrlRect);
  1093.                 InvalRect(docHScroll^^.contrlRect);
  1094.             END ELSE BEGIN
  1095.                 TEDeactivate(docTE);
  1096.                 {the controls must be hidden on deactivation:}
  1097.                 HideControl(docVScroll);
  1098.                 HideControl(docHScroll);
  1099.             END;
  1100. END; {DoActivate}
  1101.  
  1102.  
  1103. {$S Main}
  1104. PROCEDURE AdjustCursor(mouse: Point; region: RgnHandle);
  1105.  
  1106. {Change the cursor's shape, depending on its position. This also calculates a region
  1107.  that includes the cursor for WaitNextEvent.}
  1108.  
  1109. VAR
  1110.     window        : WindowPtr;
  1111.     arrowRgn    : RgnHandle;
  1112.     iBeamRgn    : RgnHandle;
  1113.     iBeamRect    : Rect;
  1114.  
  1115. BEGIN
  1116.     window := FrontWindow;    {we only adjust the cursor when we are in front}
  1117.     IF (NOT gInBackground) AND (NOT IsDAWindow(window)) THEN BEGIN
  1118.         {calculate regions for different cursor shapes}
  1119.         arrowRgn := NewRgn;
  1120.         iBeamRgn := NewRgn;
  1121.  
  1122.         {start with a big, big rectangular region}
  1123.         SetRectRgn(arrowRgn, extremeNeg, extremeNeg, extremePos, extremePos);
  1124.  
  1125.         {calculate iBeamRgn}
  1126.         IF IsAppWindow(window) THEN BEGIN
  1127.             iBeamRect := DocumentPeek(window)^.docTE^^.viewRect;
  1128.             SetPort(window);                    {make a global version of the viewRect}
  1129.             WITH iBeamRect DO BEGIN
  1130.                 LocalToGlobal(topLeft);
  1131.                 LocalToGlobal(botRight);
  1132.             END;
  1133.             RectRgn(iBeamRgn, iBeamRect);
  1134.             WITH window^.portBits.bounds DO
  1135.                 SetOrigin(-left, -top);
  1136.             SectRgn(iBeamRgn, window^.visRgn, iBeamRgn);
  1137.             SetOrigin(0, 0);
  1138.         END;
  1139.  
  1140.         {subtract other regions from arrowRgn}
  1141.         DiffRgn(arrowRgn, iBeamRgn, arrowRgn);
  1142.         
  1143.         {change the cursor and the region parameter}
  1144.         IF PtInRgn(mouse, iBeamRgn) THEN BEGIN
  1145.             SetCursor(GetCursor(iBeamCursor)^^);
  1146.             CopyRgn(iBeamRgn, region);
  1147.         END ELSE BEGIN
  1148.             SetCursor(arrow);
  1149.             CopyRgn(arrowRgn, region);
  1150.         END;
  1151.  
  1152.         {get rid of our local regions}
  1153.         DisposeRgn(arrowRgn);
  1154.         DisposeRgn(iBeamRgn);
  1155.     END;
  1156. END; {AdjustCursor}
  1157.  
  1158.  
  1159. {$S Main}
  1160. PROCEDURE DoEvent(event: EventRecord);
  1161.  
  1162. {Do the right thing for an event. Determine what kind of event it is, and call
  1163.  the appropriate routines.}
  1164.  
  1165. VAR
  1166.     part    : INTEGER;
  1167.     window    : WindowPtr;
  1168.     key        : CHAR;
  1169.  
  1170. BEGIN
  1171.     CASE event.what OF
  1172.         nullEvent:
  1173.             DoIdle;
  1174.         mouseDown: BEGIN
  1175.             part := FindWindow(event.where, window);
  1176.             CASE part OF
  1177.                 inMenuBar: BEGIN
  1178.                     AdjustMenus;
  1179.                     DoMenuCommand(MenuSelect(event.where));
  1180.                 END;
  1181.                 inSysWindow:
  1182.                     SystemClick(event, window);
  1183.                 inContent:
  1184.                     IF window <> FrontWindow THEN BEGIN
  1185.                         SelectWindow(window);
  1186.                         {DoEvent(event);}    {use this line for "do first click"}
  1187.                     END ELSE
  1188.                         DoContentClick(window, event);
  1189.                 inDrag:
  1190.                     DragWindow(window, event.where, screenBits.bounds);
  1191.                 inGrow:
  1192.                     DoGrowWindow(window, event);
  1193.                 inGoAway:
  1194.                     IF TrackGoAway(window, event.where) THEN
  1195.                         DoCloseWindow(window);
  1196.                 inZoomIn, inZoomOut:
  1197.                     IF TrackBox(window, event.where, part) THEN
  1198.                         DoZoomWindow(window, part);
  1199.             END;
  1200.         END;
  1201.         keyDown, autoKey: BEGIN
  1202.             key := CHR(BAnd(event.message, charCodeMask));
  1203.             IF BAnd(event.modifiers, cmdKey) <> 0 THEN BEGIN    {Command key down}
  1204.                 IF event.what = keyDown THEN BEGIN
  1205.                     AdjustMenus;            {enable/disable/check menu items properly}
  1206.                     DoMenuCommand(MenuKey(key));
  1207.                 END;
  1208.             END ELSE
  1209.                 DoKeyDown(event);
  1210.         END;                                {call DoActivate with the window and...}
  1211.         activateEvt:                        {TRUE for activate, FALSE for deactivate}
  1212.             DoActivate(WindowPtr(event.message), BAnd(event.modifiers, activeFlag) <> 0);
  1213.         updateEvt:                            {call DoUpdate with the window to update}
  1214.             DoUpdate(WindowPtr(event.message));
  1215.         osEvent:
  1216.             CASE BSR(event.message, 24) OF    {high byte of message}
  1217.                 mouseMovedMessage:
  1218.                     DoIdle;                    {mouse moved is also an idle event}
  1219.                 suspendResumeMessage: BEGIN
  1220.                     gInBackground := BAnd(event.message, resumeMask) = 0;
  1221.                     DoActivate(FrontWindow, NOT gInBackground);
  1222.                 END;
  1223.             END;
  1224.     END;
  1225. END; {DoEvent}
  1226.  
  1227.  
  1228. {$S Main}
  1229. PROCEDURE EventLoop;
  1230.  
  1231. {Get events forever, and handle them by calling DoEvent.
  1232.  Also call AdjustCursor each time through the loop.}
  1233.  
  1234. VAR
  1235.     cursorRgn        : RgnHandle;
  1236.     gotEvent        : BOOLEAN;
  1237.     event            : EventRecord;
  1238.  
  1239. BEGIN
  1240.     cursorRgn := NewRgn;        {we'll pass an empty region to WNE the first time thru}
  1241.     REPEAT
  1242.         IF gHasWaitNextEvent THEN
  1243.             gotEvent := WaitNextEvent(everyEvent, event, GetSleep, cursorRgn)
  1244.         ELSE BEGIN
  1245.             SystemTask;
  1246.             gotEvent := GetNextEvent(everyEvent, event);
  1247.         END;
  1248.         IF gotEvent THEN BEGIN
  1249.             AdjustCursor(event.where, cursorRgn);
  1250.             DoEvent(event);
  1251.             END
  1252.         ELSE
  1253.             DoIdle;
  1254.         AdjustCursor(event.where, cursorRgn);
  1255.     UNTIL FALSE;    {loop forever}
  1256. END; {EventLoop}
  1257.  
  1258.  
  1259. PROCEDURE _DataInit; EXTERNAL;
  1260.  
  1261. {This routine is automatically linked in by the MPW Linker. This external
  1262.  reference to it is done so that we can unload its segment, %A5Init.}
  1263.  
  1264.  
  1265. {$S Main}
  1266. BEGIN
  1267.     UnloadSeg(@_DataInit);    {note that _DataInit must not be in Main!}
  1268.     ForceEnvirons;            {check for some basic requirements; exits if not met}
  1269.     MaxApplZone;            {expand the heap so code segments load at the top}
  1270.  
  1271.     Initialize;                {initialize the program}
  1272.     UnloadSeg(@Initialize);    {note that Initialize must not be in Main!}
  1273.  
  1274.     EventLoop;                {call the main event loop}
  1275. END.
  1276.